<!-- eslint-disable vue/valid-v-slot -->
<template>
	<!-- 查询表单 card -->
	<SearchForm
		:search="search"
		:reset="reset"
		title="信息查询"
		:searchParam="searchParam"
		:columns="searchColumns"
		:searchCol="searchCol"
		v-if="isShowSearch"
	>
		<template #searchItem="{ prop }">
			<slot :name="prop"></slot>
		</template>
	</SearchForm>

	<!-- 表格和查询中间加入统计总览或者其他 -->
	<slot name="overview"></slot>
	<!-- 表格内容 card -->
	<el-row :gutter="tableRightSpan === 0 ? 0 : 10" class="card table-flex jk-table" ref="tableRow">
		<el-col :span="24 - tableRightSpan" class="table-col-left table">
			<div class="table">
				<!-- 表格头部 操作按钮 -->
				<div class="table-header">
					<div class="title-left-tip flex" v-if="!!title">
						<!-- 表格头部标题 -->
						<h2>{{ title }}</h2>
						<!-- 标题的其他提示插槽 -->
						<slot name="titleTip"></slot>
					</div>
					<div class="header-button-lf">
						<!-- 表格头部按钮可以传按钮、或者插槽 -->
						<slot name="tableHeader" :selectedListIds="selectedListIds" :selectList="selectedList" :isSelected="isSelected">
							<div v-if="tableHeaderBtns.length > 0">
								<el-button
									:type="item.btnType ?? 'primary'"
									:disabled="item.btnDisabled && selectedListIds.length === 0"
									:icon="item.btnIcon"
									:style="item.style"
									:size="item.size ?? 'default'"
									v-for="(item, index) in tableHeaderBtns"
									:key="index"
									@click="item.btnClick(selectedListIds)"
									>{{ item.btnName }}</el-button
								>
							</div>
						</slot>
						<el-button :icon="Operation" circle v-if="columns.length && isShowColSet" @click="openColSetting"> </el-button>
					</div>
					<!-- <div class="header-button-ri" v-if="toolButton"> -->
					<!-- <el-button :icon="Refresh" circle @click="getTableList"> </el-button> -->
					<!-- <el-button :icon="Printer" circle v-if="columns.length" @click="handlePrint"> </el-button> -->
					<!-- <el-button :icon="Operation" circle v-if="columns.length" @click="openColSetting"> </el-button> -->
					<!-- <el-button :icon="Search" circle v-if="searchColumns.length" @click="isShowSearch = !isShowSearch"> </el-button> -->
					<!-- </div> -->
				</div>
				<!-- 表格主体 -->
				<el-table
					ref="tableRef"
					v-loading="tableLoading"
					element-loading-text="加载中..."
					v-bind="$attrs"
					:highlight-current-row="highlightCurrentRow"
					:data="tableData"
					:border="border"
					:default-expand-all="defaultExpandAll"
					:row-key="getRowKeys"
					@sort-change="sortChange"
					@selection-change="selectionChange"
					:header-cell-class-name="headerCellcClassName"
					:row-class-name="rowClassName"
					@row-click="rowClick"
				>
					<!-- 默认插槽 -->
					<slot></slot>
					<template v-for="item in tableColumns" :key="item">
						<!-- selection || index -->
						<el-table-column
							v-bind="item"
							:align="item.align ?? 'center'"
							:reserve-selection="item.type == 'selection'"
							v-if="item.type == 'selection' || item.type == 'index'"
						>
						</el-table-column>
						<!-- expand 支持 tsx 语法 && 作用域插槽 (tsx > slot) -->
						<el-table-column v-bind="item" :align="item.align ?? 'center'" v-if="item.type == 'expand'" v-slot="scope">
							<component :is="item.render" :row="scope.row" v-if="item.render"> </component>
							<slot :name="item.type" :row="scope.row" v-else></slot>
						</el-table-column>
						<!-- other 循环递归 -->
						<TableColumn v-if="!item.type && item.prop && item.isShow" :column="item">
							<template v-for="slot in Object.keys($slots)" #[slot]="scope">
								<slot :name="slot" :row="scope.row" :index="scope.$index"></slot>
							</template>
						</TableColumn>
					</template>
					<!-- 无数据 -->
					<template #empty>
						<zhy-empty />
					</template>
				</el-table>
				<!-- 分页组件 -->
				<Pagination
					v-if="pagination"
					:pageable="pageable"
					:handleSizeChange="handleSizeChange"
					:handleCurrentChange="handleCurrentChange"
				/>
			</div>
		</el-col>
		<!-- 右侧内容 -->
		<el-col :span="tableRightSpan" class="table-col-right table">
			<slot name="tableRight"></slot>
		</el-col>
	</el-row>

	<!-- 列设置 -->
	<ColSetting v-if="toolButton" ref="colRef" v-model:colSetting="colSetting" />
</template>

<script setup lang="ts" name="ProTable">
import { ref, watch, provide } from 'vue';
import { useTable } from '@/hooks/useTable';
import { useSelection } from '@/hooks/useSelection';
import { BreakPoint } from '@/components/Grid/interface';
import { ColumnProps, HeaderBtnsItem } from '@/components/ProTable/interface';
import { ElTable, TableProps } from 'element-plus';
import { Operation } from '@element-plus/icons-vue';
import { handleProp } from '@/utils/util';
import SearchForm from '@/components/SearchForm/index.vue';
import ZhyEmpty from '../../components/ZhyEmpty/index.vue';
import Pagination from './components/Pagination.vue';
import ColSetting from './components/ColSetting.vue';
import TableColumn from './components/TableColumn.vue';
import { TableColumnCtx } from 'element-plus/es/components/table/src/table-column/defaults';

const tableRow = ref(null);

// 表格 DOM 元素
const tableRef = ref<InstanceType<typeof ElTable>>();

// 是否显示搜索模块
const isShowSearch = ref<boolean>(true);

interface ProTableProps extends Partial<Omit<TableProps<any>, 'data'>> {
	columns: ColumnProps[]; // 列配置项
	requestApi?: (params: any) => Promise<any>; // 请求表格数据的api ==> 非必传
	dataProps?: any; // 静态数据
	dataCallback?: (data: any) => any; // 返回数据的回调函数，可以对数据进行处理 ==> 非必传
	title?: string; // 表格标题，目前只在打印的时候用到 ==> 非必传
	pageParam?: any; // 处理不同接口列表中需要的分页参数不同
	pagination?: boolean; // 是否需要分页组件 ==> 非必传（默认为true）
	initParam?: any; // 初始化请求参数 ==> 非必传（默认为{}）
	border?: boolean; // 是否带有纵向边框 ==> 非必传（默认为true）
	toolButton?: boolean; // 是否显示表格功能按钮 ==> 非必传（默认为false）
	selectId?: string; // 当表格数据多选时，所指定的 id ==> 非必传（默认为 id）
	searchCol?: number | Record<BreakPoint, number>; // 表格搜索项 每列占比配置 ==> 非必传 { xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }
	isHeaderPage?: boolean; // 分页参数是否在请求头里面
	isShowColSet?: boolean; // 是否显示表格列的设置
	tableHeaderBtns?: HeaderBtnsItem[] | any[];
	tableRightSpan?: number; // 表格右侧按钮列占比
	headerCellcClassName?: any;
	highlightCurrentRow?: boolean; // 是否要高亮当前行
	isFirstload?: boolean; // 是否第一次加载接口
	rowClassName?: (data: { row: any; rowIndex: number }) => string; //
	isInitParam?: boolean; // 是否默认参数变化加载接口
	defaultExpandAll?: boolean; // 是否默认展开所有行
}

// 接受父组件参数，配置默认值
const props = withDefaults(defineProps<ProTableProps>(), {
	columns: () => [],
	pagination: true,
	pageParam: {
		pageNum: 'page',
		pageSize: 'limit',
		total: 'totalCount'
	},
	isShowColSet: false,
	initParam: {},
	isInitParam: true,
	border: true,
	toolButton: true,
	selectId: 'id',
	isHeaderPage: false,
	searchCol: () => ({ xs: 1, sm: 2, md: 3, lg: 4, xl: 5 }), // xs: 1代码当前屏幕一个筛选条件就会有隐藏其他条件， 后面以此类推
	tableHeaderBtns: () => [],
	tableRightSpan: 0,
	highlightCurrentRow: true,
	isFirstload: true,
	defaultExpandAll: false
});

// 表格多选 Hooks
const { selectionChange, getRowKeys, selectedList, selectedListIds, isSelected } = useSelection(props.selectId);

// 清空选中数据列表
const clearSelection = () => tableRef.value!.clearSelection();

// 监听页面 initParam 改化，重新获取表格数据
watch(
	() => props.initParam,
	val => {
		props.isInitParam && getTableList(val);
	},
	{ deep: true }
);

// 接收 columns 并设置为响应式
const tableColumns = ref<ColumnProps[]>(props.columns);

// 定义 enumMap 存储 enum 值（避免异步请求无法格式化单元格内容 || 无法填充搜索下拉选择）
const enumMap = ref(new Map<string, { [key: string]: any }[]>());
provide('enumMap', enumMap);

// 扁平化 columns && 处理 tableColumns 数据
const flatColumnsFunc = (columns: ColumnProps[], flatArr: ColumnProps[] = []) => {
	columns.forEach(async col => {
		if (col._children?.length) flatArr.push(...flatColumnsFunc(col._children));
		flatArr.push(col);

		// 给每一项 column 添加 isShow && isFilterEnum 属性
		col.isShow = col.isShow ?? true;
		col.isFilterEnum = col.isFilterEnum ?? true;

		if (!col.enum) return;

		// 如果当前 enum 为后台数据需要请求数据，则调用该请求接口，并存储到 enumMap
		if (typeof col.enum !== 'function') {
			enumMap.value.set(col.prop!, col.enum);
		} else {
			const { data } = await col.enum();
			// 过滤字典数据
			let enumData: any[] = [];
			if (col?.enumParams) {
				enumData = data.filter((item: any) => {
					return col?.enumParams === item.dicTypeCode;
				});
				console.log(enumData);
				// 过滤字典中某些值
				if (col?.search?.filterDict && col?.search?.filterDict?.length > 0) {
					enumData = enumData.filter((item: any) => {
						return !col?.search?.filterDict?.includes(item.value);
					});
				}
			} else {
				enumData = data;
			}

			enumMap.value.set(col.prop!, enumData);
		}
	});
	return flatArr.filter(item => !item._children?.length);
};

// 扁平 columns
const flatColumns = ref<ColumnProps[]>();
flatColumns.value = flatColumnsFunc(tableColumns.value as any);

const request = () => {
	return new Promise(resolve => {
		resolve(props.dataProps);
	});
};

// 表格操作 Hooks
const {
	tableData,
	pageable,
	searchParam,
	tableLoading,
	searchInitParam,
	getTableList,
	search,
	reset,
	handleSizeChange,
	handleCurrentChange
} = useTable(
	props.requestApi || request,
	props.initParam,
	props.pagination,
	props.dataCallback,
	props.pageParam,
	props.isHeaderPage,
	flatColumns.value,
	props.isFirstload
);

// 排序事件
const sortChange = ({ prop, order }: any) => {
	pageable.value = Object.assign(pageable.value, {
		orderField: prop,
		orderType: order === 'ascending' ? 'asc' : 'desc'
	});
	getTableList();
};

// 过滤需要搜索的配置项 && 处理搜索排序
const searchColumns = flatColumns.value
	.filter(item => item.search?.el ?? item.search?.type)
	.sort((a, b) => (b.search?.order ?? 0) - (a.search?.order ?? 0));
// 设置搜索表单的默认值
searchColumns.forEach(column => {
	if (column.search?.defaultValue !== undefined && column.search?.defaultValue !== null) {
		searchInitParam.value[column.search.key ?? handleProp(column.prop!)] = column.search?.defaultValue;
	}
});

// 列设置 ==> 过滤掉不需要设置显隐的列
const colRef = ref();
const colSetting = tableColumns.value!.filter(item => {
	return item.isShow && item.type !== 'selection' && item.type !== 'index' && item.type !== 'expand' && item.prop !== 'operation';
});

const openColSetting = () => {
	colRef.value.openColSetting();
};

// 获取表单数据变化
watch(
	() => props.dataProps,
	() => {
		getTableList();
	},
	{ deep: true }
);

// 获取表单数据变化
watch(
	searchParam,
	val => {
		emit('searchChange', val);
	},
	{ deep: true }
);

interface ModelEmits {
	(e: 'searchChange', value: ColumnProps): void;
	(e: 'rowClick', value: any): void;
}
const emit = defineEmits<ModelEmits>();

// 点击行事件
const rowClick = (row: any, column: TableColumnCtx<ColumnProps>) => {
	emit('rowClick', { row, column });
};

// 暴露给父组件的参数和方法(外部需要什么，都可以从这里暴露出去)
defineExpose({
	element: tableRef,
	tableData,
	searchParam,
	pageable,
	getTableList,
	clearSelection,
	selectedList
});
</script>
<style lang="scss" scoped>
.table-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	margin-bottom: 10px;
}
.jk-table {
	margin: 0 !important;
	:deep(.el-scrollbar) {
		min-height: 200px;
	}
	:deep(.warning-row) {
		--el-table-tr-bg-color: var(--el-color-warning-light-9);
	}

	// 隐藏头部的选择框
	:deep(.hidden-header-checkbox) {
		.el-checkbox {
			visibility: hidden;
		}
	}
	.table-col-left {
		padding-left: 0 !important;
	}
	.table-col-right {
		padding-right: 0 !important;
	}
}
.table-flex {
	display: flex;
	flex: 1;
	height: 100%;
}
</style>
